require_relative 'slot'
require_relative 'macrofunc'

$application_num = 0

$my_slots = Array.new(256)
for i in 0..255 
	$my_slots[i] = Slot.new i
end

$enemy_slots = Array.new(256)
for	i in 0..255
	$enemy_slots[i] = Slot.new i
end

def $my_slots.vitality
	inject(0) { |sum, x| sum += x.vitality }
end

def $enemy_slots.vitality
	inject(0) { |sum, x| sum += x.vitality }
end

$current_slots = $my_slots

$registers = [ 
		{:slot => $my_slots[0], :cost => 4},
		{:slot => $my_slots[1], :cost => 7},
		{:slot => $my_slots[2], :cost => 10}
#		{:slot => $my_slots[3], :cost => 13},
#		{:slot => $my_slots[4], :cost => 16}
	]

class CommandQueue < Array
	def push_command(slot_number, f, application)
		push Command.new(slot_number, f, application)
		#invoke_current
  end

  def push_front_command(slot_number, f, application)
		unshift Command.new(slot_number, f, application)
		#invoke_current
	end
end

$command_queue = CommandQueue.new

$invokations = 0

class Command
	attr_reader :slot_number, :f, :application
	def initialize slot_number, f, application
		@slot_number = slot_number
		@f = f
		@application = application
	end
	def to_s
		"Command (#{@slot_number}, #{@f}, #{@application})"
	end
end

class BuildNumberCommand
	attr_reader :slot, :number
	def initialize slot, number
		@slot = slot
		@number = number
	end
	
	def expand_to_queue
		queue = CommandQueue.new
		expand_build_number_macro(queue, @slot, @number)
		queue = queue.reverse
		queue.each do |c|
			$command_queue.unshift(c)
		end
	end
	
end

def is_there_commands?
  !$command_queue.empty?
end

def fetch_current_command
  command = get_and_shift_current

  return command.application == :left ? 2 : 1, command.slot_number, command.f.new.name
end

def get_and_shift_current
  command = $command_queue.shift

  if (command.respond_to?("expand_to_queue"))
	  command.expand_to_queue
	  command = $command_queue.shift
  end
  command
end

def invoke_current
  command = get_and_shift_current
  slot = $my_slots[command.slot_number]
  #puts command
  #puts slot
  case command.application
    when :left
      slot.left_apply command.f
    when :right
      slot.right_apply command.f
  end
  $invokations += 1
end

def invoke_all
	count = 0
	while !$command_queue.empty?
		invoke_current
		count += 1
	end
	puts "invoked all commands - #{count}"
end

def print_slots(slots,name)
	puts "Printing #{name} slots except {10000,I}: "
	i = 0;
	slots.each do |s|
		puts "#{i}=#{s.to_s}" if !(s.vitality == 10000 && s.field.respond_to?('name') && s.field.name == 'I')
		i+=1
	end
end

def swap_slots
	$my_slots, $enemy_slots = $enemy_slots, $my_slots
end

def path(n, cur)
  steps = Array.new
  raise "Can't decrement" if n < cur
  case n
    when 0
      return steps
    when 1
      steps.push Succ
    else
      while n > cur do
        if (n % 2 != 0 || n / 2 < cur)
          steps.push Succ
	  n -= 1
	elsif
	  steps.push Dbl
	  n /= 2
	end
      end
  end
  steps
end

$prefered = (0..255).to_a.
	map { |i| { :cell => i, :path => path(i, 0) } }.
	sort! { |a, b| a[:path].length <=> b[:path].length }.
	find_all { |a| a[:cell] >= $registers.size }.
	map { |a| a[:cell] }

def find_vacant
	$prefered.find do |i|
		!$my_slots[i].dead? && 
			$my_slots[i].field.respond_to?("name") && 
			($my_slots[i].field.name == "I")
	end
end

def get_card_by_name n
  case n
    when "I"
      I
    when "zero"
      Zero
    when "succ"
      Succ
    when "dbl"
      Dbl
    when "get"
      Get
    when "put"
      Put
    when "S"
      S
    when "K"
      K
    when "inc"
      Inc
    when "dec"
      Dec
    when "attack"
      Attack
    when "help"
      Help
    when "copy"
      Copy
    when "revive"
      Revive
    when "zombie"
      Zombie
  end
end

def do_application(lr, slot, card)
  begin
    case lr
      when 2
        $my_slots[slot].left_apply(get_card_by_name card)
      when 1
        $my_slots[slot].right_apply(get_card_by_name card)
    end
  rescue => e
    #... just do nothing, we don't want bad application to break demon
    #raise e
  end
end